﻿using Microsoft.EntityFrameworkCore;
using Nantianmen.Core.Stores;
using Nantianmen.Domain.Models;
using Nantianmen.Shared;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nantianmen.EntityFrameworkCore
{
    public class RouteStore : RouteStoreBase, IRouteStore, IQueryableRouteStore
    {
        public RouteStore(DbContext context, NantianmenErrorDescriber? errorDescriber = null) : base(errorDescriber ?? new NantianmenErrorDescriber())
        {
            ArgumentNullException.ThrowIfNull(context);
            Context = context;
        }

        public virtual DbContext Context { get; }

        protected DbSet<Route> RoutesSet => Context.Set<Route>();

        public bool AutoSaveChanges { get; set; } = true;
        protected Task SaveChanges(CancellationToken cancellationToken)
        {
            return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.CompletedTask;
        }

        public override IQueryable<Route> Routes => RoutesSet;

        public override async Task<bool> CheckDuplicateRouteIdAsync(string routeId, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            ArgumentNullException.ThrowIfNullOrWhiteSpace(routeId);

            var query = from route in RoutesSet
                        where route.Id == routeId
                        select route;

            return (await query.CountAsync(cancellationToken)) > 0;
        }

        public override async Task<NantianmenResult> CreateAsync(Route route, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            ArgumentNullException.ThrowIfNull(route);

            Context.Add(route);
            await SaveChanges(cancellationToken);
            return NantianmenResult.Success;
        }

        public override Task<Route?> FindByIdAsync(string routeId, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            ArgumentNullException.ThrowIfNullOrWhiteSpace(routeId);

            return RoutesSet.FindAsync(routeId, cancellationToken).AsTask();
        }

        public override async Task<List<Route>> ListRoutesAsync(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            var query = Routes;
            return await query.ToListAsync(cancellationToken).ConfigureAwait(false);
        }

        public override async Task<NantianmenResult> RemoveAsync(Route route, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            ArgumentNullException.ThrowIfNull(route);

            Context.Remove(route);

            await SaveChanges(cancellationToken);

            return NantianmenResult.Success;
        }

        public override async Task<NantianmenResult> UpdateAsync(Route route, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();
            ArgumentNullException.ThrowIfNull(route);

            Context.Attach(route);
            Context.Update(route);

            await SaveChanges(cancellationToken).ConfigureAwait(false);
            return NantianmenResult.Success;
        }
    }
}
